
#ifndef VOXELIZATION_H
#define VOXELIZATION_H

#include "Vec4.h"
#include "Aabb.h"
#include "Model.h"
#include "Geometry.h"
#include "KdTree.h"

#include <FracturePiece.h>

#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>


class Model;
using namespace std;


class Voxelization
{
	
public:
	
	// This class generates a voxelization of an input reference Model, and then procedurally fractures it based on the painted texture supplied with the Model.
	
	/* Input:
	 
		 - referenceModel: the mesh to be voxelized and procedurally fractured
		 
		 - gridResolution: number of voxels desired along the longest edge of the model bounding box 
		 (other edges' resolution set by requiring voxels to be almost cubic)
		 
		 - weightMargin:  How "different" painted colors must be to count as separate fracture pieces. For a colored texture, the difference
		 actually means the difference between color "hues" (i.e. H in HSV) in [0,1]. For a greyscale texture, it means the difference between the 
		 greyscale values in [0,1].
		 
		 - smoothingIterations: How many times to apply a smoothing filter to the broken surfaces of the fracture pieces, to help hide the discrete voxelization.		    
	 */
	Voxelization(const Model& referenceModel, int gridResolution, float weightMargin, int smoothingIterations);
	
	
	// Call this to do the procedural fracture, and generate as output a set of disconnected geometries with full vertex, normal, & UV data.
	void buildFractureGeometries(vector<Geometry>& geometries);
	
	
	// We construct a grid of cells/voxels, with m_cageResolution[3] voxels per edge.
	// The lattice/cage of vertices defined by the (shared) corners of these voxels has m_cageResolution[3]+1 vertices per edge.
	// We use index ci to mean cell/voxel index, ranging in [0, m_cageResolution[i]-1],
	//    and index vi to mean lattice/cage index, ranging in [0, m_cageResolution[i]]
	
	struct Voxel
	{
		Voxel(int voxelIndex) : m_active(false), m_exterior(false), m_weight(0.0f), m_voxelIndex(voxelIndex),
		m_fracturePiece(-1)
		{
			
		}
		
		Voxel() {}
		
		// The index of this voxel in the master grid
		int m_voxelIndex;
		
		// Whether this voxel is part of "mesh interior" voxelization or not
		bool m_active;
		
		// Flags voxel as exterior. Used during initial voxelization.
		bool m_exterior;
		
		// Vertex color endowed by embedded mesh.
		float m_weight;
		
		// Index of fracture piece this voxel belongs to (if active)
		int m_fracturePiece;
	};
	
	// The master grid containing all m_cageResolution[0]*m_cageResolution[1]*m_cageResolution[2] voxels
	vector<Voxel> m_masterGrid;
	//mutable map<int, Voxel> m_masterGrid; //(using a map is more memory efficient(allows bigger grids), but also much slower)
	
	// The master grid of vertex positions. These form a lattice, until modified by fracture piece smoothing.
	vector<Vec4> m_masterVertices;
	//mutable map<int, Vec4> m_masterVertices;
	
	// Lattice/cage indices of vertices on boundary of voxelization (these are the ones which will be snapped to the mesh)
	set<int> m_boundaryVertices;
	
	// Maps lattice index of a boundary vertex to the normals/UVs, obtained by projection+interpolation from the reference mesh
	map<int, Vec4> m_boundaryNormals;
	map<int, Vec4> m_boundaryUVs;
	
	// The generated fracture pieces
	vector<FracturePiece> m_fracturePieces;
	
	const Model& m_referenceModel;
	
	int m_gridResolution;
	int m_cageResolution[3];
	float m_voxelEdges[3];
	Aabb m_meshAabb;
	KdTree* m_meshKdTree;
	
	float m_weightMargin;
	int m_smoothingIterations;
	
	int m_numActiveVoxels;
	
	/// Utility functions
	
	// Returns the position of a given lattice point given its lattice indices (NB, will not actually be on grid after smoothing)
	Vec4 _latticeVertex(int vi, int vj, int vk) const;
	
	// Update the lattice positions
	void _setLatticeVertex(int latticeIndex, const Vec4& P);
	
	// Return index into vertex array, given vertex lattice coords each in range [0, m_cageResolution[i]]
	int _latticeIndex(int vi, int vj, int vk) const;
	
	// Return voxel index (into the master grid array), given voxel indices
	int _voxelIndex(int ci, int cj, int ck) const;
	
	// Given the lattice index of a lattice point, return its lattice indices
	void _vertexCoords(int latticeIndex, int &vi, int &vj, int& vk) const;
	
	// Given the index of a voxel into the master grid, return its voxel indices
	void _voxelCoords(int voxelIndex, int& ci, int&cj, int& ck) const;
	
	// Return voxel indices of the voxel containing a given point 
	inline void _getVoxelContaining(const Vec4& point, int& ci, int& cj, int& ck) const;
	
	// Compute the AABB of a given voxel, given its voxel indices
	Aabb _computeVoxelAabb(int ci, int cj, int ck) const;
	
	Vec4 getCornerVertex(int corner, int ci, int cj, int ck) const;
		
	/// Base voxelization generation
	void generateVoxelization();
	inline void _addBoundaryVoxel(int ci, int cj, int ck, vector<int>& inactiveBoundaryVoxels);
	inline void _testNeighbour_floodFillExteriorVoxels(int ni, int nj, int nk, std::queue<int>& Q);
	void floodFillExteriorVoxels(const vector<int>& seedExteriorVoxels);
	
	/// Fracture piece generation
	void generateFractureSeeds();
	void buildFracturePieces();
	
	/// Mesh generation
	struct Corner
	{
		int m_latticeCoord[3];
	};
	int _cornerVertex(const Corner corner) const;
	void _addVoxelFace(int ci, int cj, int ck, const Corner corners[8], vector<Geometry::Triangle>& triangles) const;
	bool _adjacentVoxelInFracturePiece(int ci, int cj, int ck, int dci, int dcj, int dck, int fracturePieceIndex);
	void generateMeshes();
	void projectOntoReferenceMesh();

};



#endif // VOXELIZATION_H